﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Threading;
using System.Web.Caching;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MvcSupplantCaching.UnitTests
{
    [TestClass]
    public class SupplantCacheTest
    {
        private SupplantCache _cache;
        private const string Key = "test-key";
        private const string AggregateKey = "aggregate";
        private const string RegionName = null;
        private const int LongExpirationSeconds = 300;
        private const int ShortExpirationTimeSeconds = 2;

        [TestInitialize]
        public void Initialize()
        {
            _cache = SupplantCache.Cache;
        }

        [TestCleanup]
        public void Cleanup()
        {
            // make sure we flush the cache after every test
            foreach (var cacheItem in SupplantCache.Cache)
            {
                SupplantCache.Cache.Remove(cacheItem.Key);
            }            
        }

        private void SetCacheItemAndValidate(System.Runtime.Caching.CacheItem item, CacheItemPolicy policy, string regionName = null, bool testExpiration = false)
        {
            _cache.Set(item, policy);
            var cachedItem = _cache.GetCacheItem(item.Key, regionName);
            Assert.IsTrue(item.Key == cachedItem.Key && item.RegionName == cachedItem.RegionName && item.Value == cachedItem.Value);

            if (testExpiration)
            {
                var seconds = (policy.AbsoluteExpiration - DateTime.UtcNow).Seconds + 1;
                Thread.Sleep(seconds * 1000);
                cachedItem = _cache.GetCacheItem(item.Key, regionName);
                Assert.IsNull(cachedItem);
            }
        }

        private void SetCacheItemFromKeyAndValidate(System.Runtime.Caching.CacheItem item, string key, DateTimeOffset absoluteExpiration, string regionName = null, bool testExpiration = false)
        {
            _cache.Set(key, item.Value, absoluteExpiration, regionName);
            var cachedItem = _cache.GetCacheItem(key, regionName);
            Assert.IsTrue(item.Key == cachedItem.Key && item.RegionName == cachedItem.RegionName && item.Value == cachedItem.Value);

            if (testExpiration)
            {
                var seconds = (absoluteExpiration - DateTime.UtcNow).Seconds + 1;
                Thread.Sleep(seconds * 1000);
                cachedItem = _cache.GetCacheItem(key, regionName);
                Assert.IsNull(cachedItem);
            }
        }

        private void SetCacheItemFromKeyAndValidate(System.Runtime.Caching.CacheItem item, string key, CacheItemPolicy policy, string regionName = null, bool testExpiration = false)
        {
            _cache.Set(key, item.Value, policy, regionName);
            var cachedItem = _cache.GetCacheItem(key, regionName);
            Assert.IsTrue(item.Key == cachedItem.Key && item.RegionName == cachedItem.RegionName && item.Value == cachedItem.Value);

            if (testExpiration)
            {
                var seconds = (policy.AbsoluteExpiration - DateTime.UtcNow).Seconds + 1;
                Thread.Sleep(seconds * 1000);
                cachedItem = _cache.GetCacheItem(key, regionName);
                Assert.IsNull(cachedItem);
            }
        }

        [TestMethod]
        public void SupplantCacheSetCacheItemTest()
        {
            var cacheItem = new System.Runtime.Caching.CacheItem(Key, "blah", RegionName);
            var policy = new CacheItemPolicy
            {
                AbsoluteExpiration = DateTime.UtcNow.AddSeconds(LongExpirationSeconds)
            };
            SetCacheItemAndValidate(cacheItem, policy, RegionName);
        }

        [TestMethod]
        public void SupplantCacheWithKeyAndExpirationTest()
        {
            var cacheItem = new System.Runtime.Caching.CacheItem(Key, "blah", RegionName);
            SetCacheItemFromKeyAndValidate(cacheItem, Key, DateTime.UtcNow.AddSeconds(ShortExpirationTimeSeconds), RegionName, true);
        }

        [TestMethod]
        public void SupplantCacheWithKeyAndCachePolicyTest()
        {
            var cacheItem = new System.Runtime.Caching.CacheItem(Key, "blah", RegionName);
            var policy = new CacheItemPolicy
            {
                AbsoluteExpiration = DateTime.UtcNow.AddSeconds(ShortExpirationTimeSeconds)
            };
            SetCacheItemFromKeyAndValidate(cacheItem, Key, policy, RegionName, true);
        }

        private void AddCacheItems(string keyBase)
        {          
            // create a compare list of items and cache each item
            var itemList = new List<System.Runtime.Caching.CacheItem>(10);
            for (int i = 0; i < 10; i++)
            {
                var key = keyBase + i;
                _cache.Set(key,i,new CacheItemPolicy(),null);
                itemList.Add(_cache.GetCacheItem(key,null));
            }            
        }

        [TestMethod]
        public void AggregateTest()
        {
            CreateAggregateCacheAndValidate();
        }

        private void CreateAggregateCacheAndValidate()
        {
            AddCacheItems(Key);

            // aggregate all items
            var aggregatedCache = _cache.AggregateCache(AggregateKey, DateTime.UtcNow.AddYears(1), string.Empty).CacheItems;

            Assert.IsTrue(_cache.Count() == 1);
            Assert.IsTrue(_cache.Contains(AggregateKey));

            // make sure the list stored is the same as the list that was returned
            var cacheContainer = (SupplantCacheItemContainer)_cache[AggregateKey];
            Assert.IsTrue(cacheContainer.CacheItems.Equals(aggregatedCache));
        }

        [TestMethod]
        public void AggregateIgnorePrefixTest()
        {
            CreateAggregateCacheAndValidate();

            const string newkey = AggregateKey + "2";

            AddCacheItems(Key + Key);

            // aggregate all items
            var aggregatedCache = _cache.AggregateCache(newkey, DateTime.UtcNow.AddYears(1), string.Empty).CacheItems;

            Assert.IsTrue(_cache.Count() == 2);
            Assert.IsTrue(_cache.Contains(AggregateKey));
            Assert.IsTrue(_cache.Contains(newkey));

            // make sure the list stored is the same as the list that was returned
            var cacheContainer = (SupplantCacheItemContainer)_cache[AggregateKey];
            Assert.IsTrue(!cacheContainer.CacheItems.Equals(aggregatedCache));

            cacheContainer = (SupplantCacheItemContainer)_cache[newkey];
            Assert.IsTrue(cacheContainer.CacheItems.Equals(aggregatedCache));
        }
    }
}
